package sharding_selector

import (
	"context"
	"database/sql"
	"fmt"
	errors2 "gitee.com/go-web-2/orm_framework/errors"
	"gitee.com/go-web-2/orm_framework/middleware"
	"gitee.com/go-web-2/orm_framework/predicate"
	"gitee.com/go-web-2/orm_framework/session/db/sharding_DB"
	"gitee.com/go-web-2/orm_framework/session/model"
	value2 "gitee.com/go-web-2/orm_framework/session/value"
	sql2 "gitee.com/go-web-2/orm_framework/sql"
	"gitee.com/go-web-2/orm_framework/sql/common"
	"gitee.com/go-web-2/orm_framework/utils"
	"golang.org/x/sync/errgroup"
	"reflect"
	"sync"
)

/*
* 该shardingSelector只提供简单功能：
* 不支持范围查询，比如user_id < 5；不支持JOIN、子查询等； table不支持别名；不支持异构数据库
* 支持=、and、or; 支持聚合查询；可支持（暂不实现）分页、排序、去重等；暂未实现get方法，只实现getMutli方法
 */

type Selector[T any] struct {
	selects []predicate.Selectable
	table   predicate.Table
	wheres  []predicate.Predicate

	middlewares []middleware.Middleware
	common.SentenceBuilder

	shardingDB   *sharding_DB.ShardingDB
	ShardingKeys map[string]string
	ShardingFunc model.ShardingFunc

	mu sync.Mutex
}

// findDstList 找到分库分表的所有目标节点
func (s *Selector[T]) findDstList() ([]*model.Dst, error) {
	pd := s.wheres[0]
	if len(s.wheres) > 1 {
		for _, p := range s.wheres {
			pd = predicate.Predicate{
				Left:  pd,
				Op:    predicate.OpAnd,
				Right: p,
			}
		}

	}

	dstS, isBroadcast, err := s.findDstListByPredicate(pd)
	if err != nil {
		return nil, err
	}
	if isBroadcast {
		return s.Model.ShardingFunc.AllNodes(), nil
	}
	return dstS, nil
}

func (s *Selector[T]) getOpEqualDstS(p predicate.Predicate) ([]*model.Dst, bool, error) {
	// 左侧是shardingKey就找库表，不是就需要返回allNodes(广播)

	c, ok := p.Left.(predicate.Column)
	if !ok {
		return nil, false, errors2.ErrUnSupportShardingPredicate(p)
	}
	var v predicate.Valuer
	v, ok = p.Right.(predicate.Valuer)
	if !ok {
		return nil, false, errors2.ErrUnSupportShardingPredicate(p)
	}

	_, ok = s.Model.ShardingKeys[c.ColumnName]
	if !ok {
		// 广播
		return nil, true, nil
	}

	dst, err := s.Model.ShardingFunc.Sharding(v.Val)
	if err != nil {
		return nil, false, err
	}
	return []*model.Dst{dst}, false, nil
}

func (s *Selector[T]) getOpAndDstS(p predicate.Predicate) ([]*model.Dst, bool, error) {
	leftP, ok := p.Left.(predicate.Predicate)
	if !ok {
		return nil, false, errors2.ErrUnSupportShardingPredicate(p)
	}
	var rightP predicate.Predicate
	rightP, ok = p.Right.(predicate.Predicate)
	if !ok {
		return nil, false, errors2.ErrUnSupportShardingPredicate(p)
	}

	leftDstS, isLeftBroadCast, err := s.findDstListByPredicate(leftP)
	if err != nil {
		return nil, false, err
	}
	if isLeftBroadCast {
		// 左侧是广播，直接返回右侧结果
		return s.findDstListByPredicate(rightP)
	}

	var rightDstS []*model.Dst
	var isRightBroadCast bool
	rightDstS, isRightBroadCast, err = s.findDstListByPredicate(rightP)
	if err != nil {
		return nil, false, err
	}
	// 左侧不是广播，右侧是广播，返回左侧结果
	if isRightBroadCast {
		return leftDstS, false, nil
	}

	// 左右两侧都不是广播，取交集
	// 算法优化：使用map结构查找两个数组的交集
	rightMap := make(map[string]struct{}, len(rightDstS))
	for _, dst := range rightDstS {
		key := dst.DbName + "." + dst.TableName
		rightMap[key] = struct{}{}
	}
	res := make([]*model.Dst, 0, len(rightDstS))
	for _, dst := range leftDstS {
		key := dst.DbName + "." + dst.TableName
		_, ok = rightMap[key]
		if ok {
			res = append(res, dst)
		}
	}

	return res, false, nil
}

func (s *Selector[T]) getOpOrDstS(p predicate.Predicate) ([]*model.Dst, bool, error) {
	leftP, ok := p.Left.(predicate.Predicate)
	if !ok {
		return nil, false, errors2.ErrUnSupportShardingPredicate(p)
	}
	var rightP predicate.Predicate
	rightP, ok = p.Right.(predicate.Predicate)
	if !ok {
		return nil, false, errors2.ErrUnSupportShardingPredicate(p)
	}

	leftDstS, isLeftBroadCast, err := s.findDstListByPredicate(leftP)
	if err != nil {
		return nil, false, err
	}
	if isLeftBroadCast {
		// 左侧是广播，直接返回广播
		return nil, true, nil
	}

	var rightDstS []*model.Dst
	var isRightBroadCast bool
	rightDstS, isRightBroadCast, err = s.findDstListByPredicate(rightP)
	if err != nil {
		return nil, false, err
	}
	// 左侧不是广播，右侧是广播，返回广播
	if isRightBroadCast {
		return nil, true, nil
	}

	// 左右两侧都不是广播，取并集
	res := leftDstS
	for _, leftDst := range leftDstS {
		for _, rightDst := range rightDstS {
			if rightDst.DbName != leftDst.DbName || rightDst.TableName != rightDst.TableName {
				res = append(res, rightDst)
			}
		}
	}
	return res, false, nil
}

func (s *Selector[T]) findDstListByPredicate(p predicate.Predicate) ([]*model.Dst, bool, error) {
	switch p.Op {
	case predicate.OpEqual:
		return s.getOpEqualDstS(p)
	case predicate.OpAnd:
		return s.getOpAndDstS(p)
	case predicate.OpOr:
		return s.getOpOrDstS(p)
	default:
		return nil, false, errors2.ErrUnSupportShardingOperation(p.Op.String())
	}
}

func (s *Selector[T]) buildSelect() error {
	s.Builder.WriteString("select ")
	if len(s.selects) > 0 {
		for i, item := range s.selects {
			// select 部分能够定义别名，不能使用别名；别名将在其他部分比如order by中使用
			err := s.BuildExpression(item.(predicate.Expression), sql2.UseAlias{Build: true, Use: false})
			if err != nil {
				return err
			}
			if i < len(s.selects)-1 {
				s.Builder.WriteString(",")
			}
		}
	} else {
		s.Builder.WriteString("*")
	}

	return nil
}

func (s *Selector[T]) buildWhere() error {
	if len(s.wheres) > 0 {
		s.Builder.WriteString(" where ")
		for i, item := range s.wheres {
			// where处不能定义、使用别名
			err := s.BuildPredicate(item, sql2.UseAlias{Build: false, Use: false})
			if err != nil {
				return err
			}

			if i != len(s.wheres)-1 {
				s.Builder.WriteString(" " + predicate.OpAnd.String() + " ")
			}
		}
	}

	return nil
}

func (s *Selector[T]) buildFrom(dbName string, tableName string) error {
	s.Builder.WriteString(" from ")
	s.Builder.WriteString(fmt.Sprintf("%s.%s", s.GenerateField(dbName), s.GenerateField(tableName)))
	return nil
}

func (s *Selector[T]) build(dst *model.Dst) (*sql2.Query, error) {
	err := s.buildSelect()
	if err != nil {
		return nil, err
	}

	err = s.buildFrom(dst.DbName, dst.TableName)
	if err != nil {
		return nil, err
	}

	err = s.buildWhere()
	if err != nil {
		return nil, err
	}

	s.Builder.WriteString(";")
	return &sql2.Query{
		DB:   dst.DbName,
		SQL:  s.Builder.String(),
		Args: s.Args,
	}, nil
}

func (s *Selector[T]) Build() ([]*sql2.Query, error) {
	if s.Model == nil {
		var t *T
		s.Model = s.R.Get(t).SetSharding(s.ShardingKeys, s.ShardingFunc)
	}

	dstList, err := s.findDstList()
	if err != nil {
		return nil, err
	}
	res := make([]*sql2.Query, 0, len(dstList))
	for _, dst := range dstList {
		var query *sql2.Query
		query, err = s.build(dst)
		if err != nil {
			return nil, err
		}
		res = append(res, query)

		// 清空数据
		s.Builder.Reset()
		s.Args = make([]interface{}, 0)
	}
	return res, nil
}

//func (s *Selector[T]) Get(ctx context.Context, val interface{}) *sql2.QueryResult {
//queryList, err := s.Build()
//if err != nil {
//	return &sql2.QueryResult{Err: err}
//}
//
//var root middleware.Handler = func(c context.Context, qc *sql2.QueryContext) *sql2.QueryResult {
//	var rows *sql.Rows
//	rows, err = s.sess.QueryContext(ctx, query.SQL, query.Args...)
//	if err != nil {
//		return &sql2.QueryResult{Err: err}
//	}
//
//	defer rows.Close()
//	if !rows.Next() {
//		return &sql2.QueryResult{Err: errors2.ErrNoRows}
//	}
//
//	var value value2.Value
//
//	model := s.R.Get(val) // .SetSharding(s.ShardingKeys,s.ShardingFunc)
//	value, err = s.ValueCreator(val, model)
//	if err != nil {
//		return &sql2.QueryResult{Err: err}
//	}
//	err = value.SetColumns(rows, s.Dialect.Quote())
//	if err != nil {
//		return &sql2.QueryResult{Err: err}
//	}
//	return &sql2.QueryResult{
//		Res: val,
//	}
//}
//
//for _, m := range s.middlewares {
//	root = m(root)
//}
//return root(ctx, &sql2.QueryContext{
//	Type:    "Selector",
//	Builder: s,
//	Model:   s.Model,
//})
//}

func (s *Selector[T]) GetMutli(ctx context.Context, vals interface{}) *sql2.QueryResult {
	elem, err := utils.ValidateValsType(vals)
	if err != nil {
		return &sql2.QueryResult{Err: err}
	}

	var root middleware.Handler = func(c context.Context, qc *sql2.QueryContext) *sql2.QueryResult {
		var queryList []*sql2.Query
		queryList, err = s.Build()
		if err != nil {
			return &sql2.QueryResult{Err: err}
		}

		wg := &errgroup.Group{}

		for _, query := range queryList {
			var rows *sql.Rows
			q := query // 注意哦
			wg.Go(func() error {
				db2, ok := s.shardingDB.DBs[q.DB]
				if !ok {
					// 可能用户配置不对、可能代码不对
					return errors2.ErrUnknownShardingDB(q.DB)
				}
				rows, err = db2.QueryContext(ctx, q.SQL, q.Args...)
				if err != nil {
					return err
				}

				valInstance := reflect.New(elem).Interface() // 取值
				m := s.R.Get(valInstance).SetSharding(s.ShardingKeys, s.ShardingFunc)

				var r *value2.ReflectValues
				// model不能在里面生成,需要外面传入原因: 使用s.R中的缓存数据
				s.mu.Lock() // append vals 不能冲突
				defer s.mu.Unlock()
				r, err = value2.NewReflectValues(vals, m, value2.WithValueCreator(s.ValueCreator))

				if err != nil {
					return err
				}
				err = r.SetValues(rows, s.Dialect.Quote())
				if err != nil {
					return err
				}

				return nil
			})
		}
		err = wg.Wait()
		// 对所有vals合并处理
		return &sql2.QueryResult{
			Res: vals,
			Err: err,
		}
	}

	return root(ctx, &sql2.QueryContext{
		Type:    "Selector",
		Builder: s,
		Model:   s.Model,
	})
}

// Select 支持Column、聚合函数、raw(自定义输入)
func (s *Selector[T]) Select(selectable ...predicate.Selectable) *Selector[T] {
	s.selects = append(s.selects, selectable...)
	return s
}

// From 支持表名、子查询、聚合查询
func (s *Selector[T]) From(table predicate.Table) *Selector[T] {
	s.table = table
	return s
}

func (s *Selector[T]) Where(where ...predicate.Predicate) *Selector[T] {
	s.wheres = append(s.wheres, where...)
	return s
}

type Config struct {
	ShardingDB   *sharding_DB.ShardingDB
	ShardingKeys map[string]string
	ShardingFunc model.ShardingFunc
}

func NewShardingSelector[T any](config Config, middlewares ...middleware.Middleware) *Selector[T] {
	return &Selector[T]{
		shardingDB:  config.ShardingDB,
		middlewares: middlewares,
		SentenceBuilder: common.SentenceBuilder{
			Core: config.ShardingDB.GetCore(),
		},
		ShardingKeys: config.ShardingKeys,
		ShardingFunc: config.ShardingFunc,
	}
}

//var _ sql2.Querior[any] = (*Selector[any])(nil)
